package com.hatricks.is.redission.distributedlock;

import com.hatricks.is.redission.ILock;
import com.hatricks.is.redission.ILockKeyEnum;
import com.hatricks.is.redission.KeyLock;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;

import java.util.concurrent.TimeUnit;

@Slf4j
public class DistributedKeyLock<K> implements KeyLock<K> {
    private final RedissonClient redissonClient;
    static final long DEFAULT_SECONDS = 30;
    private final String prefix;

    public DistributedKeyLock(RedissonClient redissonClient) {
        this.redissonClient = redissonClient;
        this.prefix = "REDIS:DISTRIBUTED:LOCK:";
    }

    public DistributedKeyLock(RedissonClient redissonClient, String prefix) {
        this.redissonClient = redissonClient;
        this.prefix = prefix;
    }

    @Override
    public void lock(K key, String... args) {
        RLock lock = getLock(key, args);
        if (lock != null) {
            lock.lock(getSeconds(key), TimeUnit.SECONDS);
        }
    }

    @Override
    public void unlock(K key, String... args) {
        RLock lock = getLock(key, args);
        if (lock != null) {
            lock.unlock();
        }
    }

    @Override
    public ILock getLock(K key, String... args) {
        RLock lock;
        if (key instanceof ILockKeyEnum) {
            lock = this.redissonClient.getLock(getKey(((ILockKeyEnum) key).getKey(), args));
            return new DistributedLock(lock, (ILockKeyEnum) key);
        } else {
            lock = this.redissonClient.getLock(getKey(key.toString(), args));
            return new DistributedLock(lock);
        }
    }

    private String getKey(String preKey, String... args) {
        StringBuilder key = new StringBuilder(prefix + ":" + preKey);
        if (args != null && args.length > 0) {
            for (String arg : args) {
                key.append(":").append(arg);
            }
        }
        return key.toString();
    }

    private long getSeconds(K key) {
        if (key instanceof ILockKeyEnum) {
            return ((ILockKeyEnum) key).getSeconds();
        } else {
            return DEFAULT_SECONDS;
        }
    }

}
